<?php
class Zing_Cache_PureMemcache implements Zing_Cache_Interface
{
    const EXPIRE_DEFAULT = 2592000; // 30 ngay
    private $_local_cache = array();
    private $_key_misses = array();

    private $_server = '';
    private $_port = '';
    private $_memcache = null;
    private $_profiler = null;
    private $_profiler_enable = false;

    private $_warmup = null;



    function __construct($server='127.0.0.1',$port='11211', $profiler_enable = false,$warmup = null)
    {
    	$this->_memcache = new Memcache();
		$this->_memcache->addServer($server, $port, true);

    	$this->_server = $server;
    	$this->_port = $port;
    	if($profiler_enable)
    	{
    		$this->_profiler_enable = true;
    		$this->_profiler = new Zing_Cache_Helper_Profiler();
    	}

		if($warmup != null)
		{
			$this->_warmup = $warmup;
		}
    }

    public function flushLocalCache()
    {
		foreach ($this->_local_cache as $key => &$value)
		{
		   	unset($this->_local_cache[$key]);
		}

		foreach ($this->_key_misses as $key => &$value)
		{
			unset($this->_key_misses[$key]);
		}
    }

    public function increment($key,$value = 1)
    {
        return $this->_memcache->increment($key,$value);
    }
    public function getMultiCache($keys)
    {
		//load cache from local cache first
		$local_cache = array();
		$key_miss = array();
		if(is_array($keys) && count($keys) > 0)
		{
			for($i=0;$i<count($keys);$i++)
			{
				$key = $keys[$i];
				if(array_key_exists($key, $this->_local_cache))
				{
					$local_cache[$key] = $this->_local_cache[$key];
				}
				else
				{
					$key_miss[] = $key;
				}
			}
		}

    	if(!empty($this->_warmup))
		{
			$warmup_result = $this->getCacheMultiFromSyncCache($key_miss);
			if(is_array($warmup_result) && count($warmup_result) > 0)
			{
				$key_miss2 = array();
				for($i=0;$i<count($key_miss);$i++)
				{
					$key = $key_miss[$i];
					if(array_key_exists($key,$warmup_result) && $warmup_result[$key] !== FALSE)
					{
						$this->_local_cache[$key] = $warmup_result[$key];
						$local_cache[$key] = $warmup_result[$key];
					}
					else
					{
						$key_miss2[] = $key;
					}
				}
				$key_miss = $key_miss2;
			}
		}

	   	//$cache = Globals::getCache();
	   	$cache = $this->_memcache;
	   	$starttime = gettimeofday(true);
	   	$result = $cache->get($keys);
	   	$endtime = gettimeofday(true);

	   	if($result != null && is_array($result))
	   	{
	   		$time_average = $endtime - $starttime;
	   		$time_average = $time_average / count($result);
	   		foreach ($result as $key => &$value)
		    {
		    	if($this->_profiler_enable) $this->_profiler->pushToCacheProfiler($key . " (multikey)",$value,0,$time_average);

				 $this->_local_cache[$key] = $value;
			}
	   	}
		else $result = array();

		$result = array_merge($result,$local_cache);

	   	return $result;
    }

    public function getCache($key)
    {
		if($this->_checkKeyMisses($key)) return null;

	    if(is_array($this->_local_cache) && isset($this->_local_cache[$key]))
	    {
			$cache = $this->_local_cache[$key];
		    if($cache != null) return $cache;
	    }

    	    $result = $this->getCacheFromSyncCache($key);
	    if($result !== FALSE)
	    {
	    	$this->_local_cache[$key] = $result;
	    	return $result;
	    }

	    $cache = $this->_memcache;
	    $origin_key = $key;

	    $starttime = gettimeofday(true);
	    $result = $cache->get($key);

	    $endtime = gettimeofday(true);

	    if($result === FALSE)
	    {
			$this->_addKeyMisses($origin_key);
	    }

		$this->syncache($origin_key, $result);
	    if($this->_profiler_enable) $this->_profiler->pushToCacheProfiler($origin_key,$result,$starttime,$endtime);

	    $this->_local_cache[$key] = $result;

	    return $result;
    }

    public function deleteCache($key)
    {
		if(empty($key)) return;
		$cache = $this->_memcache;
	    if($cache == null) return null;
	    $cache->delete($key);
	    if(is_array($this->_local_cache) && isset($this->_local_cache[$key]))
	    {
			unset($this->_local_cache[$key]);
	    }
	    $this->deleteCacheWarmup($key);
    }
	public function setMultiCache($items, $expireTime = -1){
		if(empty($items))
			return false;
		$cache = $this->_memcache;
		$cache->setMulti($items,$expireTime);
	}
    public function setCache($key, $value, $expireTime = -1, $compress=0)
    {
		if(is_null($value) ) return;

		if(empty($key) )
		{
			ob_start();
			var_dump($key,$value);
			$value = ob_get_contents();
			ob_end_clean();
			Globals::dumpLog("PureMemcache - Error empty key with key,value = $value");
			return;
		}

		if($expireTime == -1)
			$expireTime = self::EXPIRE_DEFAULT;
		$cache = $this->_memcache;
		$this->_removeKeyMisses($key);

		$this->syncache($key, $value, $expireTime);

		$cache->set($key,$value, $compress, $expireTime);

		if(is_array($this->_local_cache) && isset($this->_local_cache[$key]))
		{
			unset($this->_local_cache[$key]);
		}
    }


    public function getProfilerData($cache_name = '')
    {
    	$output = "";
    	if($this->_profiler_enable)
    	{
    		$cache_name .= " " . $this->_server . ":" . $this->_port;
    		$output = $this->_profiler->getProfilerData($cache_name);
    	}
    	return $output;
    }



    //private function ////////

	private function getCacheMultiFromSyncCache($keys = array())
    {
    	if(empty($this->_warmup)) return FALSE;
    	$cache = Zing_Cache_ZingGlobalCache::getGlobalCache($this->_warmup);
    	if($cache != null)
    		return $cache->getCache($keys);
    	else return FALSE;
    }

    private function getCacheFromSyncCache($key)
    {
    	if(empty($this->_warmup)) return FALSE;
    	$cache = Zing_Cache_ZingGlobalCache::getGlobalCache($this->_warmup);
    	if($cache != null)
    		return $cache->getCache($key);
    	else return FALSE;
    }

    private function deleteCacheWarmup($key)
    {
    	if(empty($this->_warmup)) return;

    	$cache = Zing_Cache_ZingGlobalCache::getGlobalCache($this->_warmup);
    	if($cache != null)
    		$cache->deleteCache($key);
    }

	private function syncache($key, $value, $expire = -1)
    {
    	if(!empty($this->_warmup) && $value !== FALSE && !empty($key))
		{
			$cache = Zing_Cache_ZingGlobalCache::getGlobalCache($this->_warmup);
			if($cache != null)
				$cache->setCache($key, $value, $expire);
		}
    }

	private function _checkKeyMisses($key)
    {
		if(isset($this->_key_misses[$key])) return true;
	    else return false;
    }

    private function _addKeyMisses($key)
    {
	    if(!$this->_checkKeyMisses($key))
	    $this->_key_misses[$key] = "1";
    }

    private function _removeKeyMisses($key)
    {
	    if($this->_checkKeyMisses($key))
	    {
	    	unset($this->_key_misses[$key]);
	    }
    }

}


?>
